#include "Control.h"

Control* Control::singleControl = NULL;

int main(int argc, char** argv)
{
	Control* control = Control::getInstance();
	control->start(argc, argv);

	return 0;
}

int Control::readParameters(int argc, char** argv)
{
	int code = 0;

	for (int i = 1; i < argc; i++)
	{
		if (i < argc && (strcmp(argv[i], "-i") == 0))
		{
			i++;
			fileName = argv[i];
		}
		else if (i < argc && (strcmp(argv[i], "-c") == 0))
		{
			i++;
			sscanf(argv[i], "%d", &bitsToTest);
		}
		else if (i < argc && (strcmp(argv[i], "-o") == 0))
		{
			i++;
			sscanf(argv[i], "%d", &offset);
		}
		else if (i < argc && (strcmp(argv[i], "-a") == 0))
		{
			code |= ALL_CODE;
		}
		else if (i < argc && (strcmp(argv[i], "-s") == 0))
		{
			i++;
			if (i < argc && (strcmp(argv[i], "monobit") == 0))
			{
				code |= MONOBIT_CODE;
			}
			else if (i < argc && (strcmp(argv[i], "freq") == 0))
			{
				code |= FREQ_CODE;
			}
			else if (i < argc && (strcmp(argv[i], "runs") == 0))
			{
				code |= RUNS_CODE;
			}
			else if (i < argc && (strcmp(argv[i], "longestrun") == 0))
			{
				code |= LONGESTRUN_CODE;
			}
			else if (i < argc && (strcmp(argv[i], "matrix") == 0))
			{
				code |= MATRIX_CODE;
			}
			else if (i < argc && (strcmp(argv[i], "fft") == 0))
			{
				code |= FFT_CODE;
			}
			else if (i < argc && (strcmp(argv[i], "nonoverlapping") == 0))
			{
				code |= NONOVERLAPPING_CODE;
			}
			else if (i < argc && (strcmp(argv[i], "overlapping") == 0))
			{
				code |= OVERLAPPING_CODE;
			}
			else if (i < argc && (strcmp(argv[i], "universal") == 0))
			{
				code |= UNIVERSAL_CODE;
			}
			else if (i < argc && (strcmp(argv[i], "linearcomplex") == 0))
			{
				code |= LINEARCOMPLEX_CODE;
			}
			else if (i < argc && (strcmp(argv[i], "serial") == 0))
			{
				code |= SERIAL_CODE;
			}
			else if (i < argc && (strcmp(argv[i], "entropy") == 0))
			{
				code |= ENTROPY_CODE;
			}
			else if (i < argc && (strcmp(argv[i], "sums") == 0))
			{
				code |= SUMS_CPDE;
			}
			else if (i < argc && (strcmp(argv[i], "excursion") == 0))
			{
				code |= EXCURSION_CODE;
			}
			else if (i < argc && (strcmp(argv[i], "variant") == 0))
			{
				code |= VARIANT_CODE;
			}
		}
		// show help
		else if (strcmp(argv[i], "--help") == 0)
		{
			std::cout << "\nAufruf: nist_testsuite INPUT OPTION"
				<< "\nOeffnet eine Datei mit Zufallsbits und fuehrt auf einer gewuenschten Anzahl"
				<< " von Bits die gewuenschten Tests der Nist Testsuite aus."
				<< "\n INPUT: "
				<< "\n -i xxxx \t\t Inputdatei mit der zu pruefenden Bitsequenz"
				<< "\n OPTION: "
				<< "\n -c xxxx \t\t Prueft xxx Bits aus der Datei."
				<< "\n -a  \t\t Fuehrt alle 15 NIST-Test aus."
				<< "\n -o xxxx \t\t ueberspringt die ersten xxxx Byte der Inputdatei."
				<< "\n -s monobit \t\t Monobit test ausfuehren"
				<< "\n -s freq \t\t Fruency test within a Block ausfuehren"
				<< "\n -s runs \t\t Runs Test ausfuehren"
				<< "\n -s longestrun \t\t Longest Run of Ones in A Block Test ausfuehren"
				<< "\n -s matrix \t\t Binary Matrix Rank Test ausfuehren"
				<< "\n -s fft \t\t Discrete Fourier Transform Test ausfuehren"
				<< "\n -s nonoverlapping \t Non-Overlapping Template Matching Test ausfuehren"
				<< "\n -s overlapping \t Overlapping Template Matching Test ausfuehren"
				<< "\n -s universal \t\t Maurers Universal Test ausfuehren"
				<< "\n -s linearcomplex \t Linear Complexity Test ausfuehren"
				<< "\n -s serial \t\t Serial test ausfuehren"
				<< "\n -s entropy \t\t Approximate Entropy Test ausfuehren"
				<< "\n -s sums \t\t Cumulative Sums Test ausfuehren"
				<< "\n -s excursion \t\t Random Excursions Test ausfuehren"
				<< "\n -s variant \t\t Random Excursions Variant Test ausfuehren";
		}
	}
	return code;
}

void Control::start(int argc, char** argv)
{
	bool fileCheck = false;
	int code = 0;
	double testResult = 0.0;
	
	code = readParameters(argc, argv);

	while (!fileCheck)
	{
		//test file
		if (fileName != NULL)
			fileCheck = ioUnit->checkFile(fileName);

		//try to open output.txt
		if (!fileCheck)
		{
			fileCheck = ioUnit->checkFile(OUTPUTPATH_DEFAULT);
			if (fileCheck)
			{
				//std::cout << "Could not open " << fileName << ". output.txt will be open instead.";
				fileName = OUTPUTPATH_DEFAULT;
			}
		}
	}
	

	//read file, print how much bits we got for testing
	bitVector = ioUnit->readFromFile(fileName, offset, true);
	bitArraySize = ioUnit->getBitCount();

	convertVector_to_Array();

	initTests(bitArray, bitArraySize);


	if ((MONOBIT_CODE & code) == MONOBIT_CODE)
	{
		testResult = monobitTest->execute(bitsToTest);
		std::cout << "\nMonobittest P_value: " << testResult ;
		if (testResult > 0.01)
			std::cout << "\nMonobittest passed.\n";
	}
	if ((FREQ_CODE & code) == FREQ_CODE)
	{
		testResult = blockfrequencyTest->execute(128, bitsToTest);
		std::cout << "\nBlock_frequency P_value: " << testResult;
		if (testResult > 0.01)
			std::cout << "\nFrequency within a Block Test  passed.\n";
	}
	if ((RUNS_CODE & code) == RUNS_CODE)
	{
		testResult = runsTest->execute(bitsToTest);
		std::cout << "\nRuns P_value: " << testResult;
		if (testResult > 0.01)
			std::cout << "\nRuns Test passed.\n";
	}
	if ((LONGESTRUN_CODE & code) == LONGESTRUN_CODE)
	{
		testResult = longestRunOfOnesTest->execute(bitsToTest);
		std::cout << "\nP_value for LongestRunOfOnes: " << testResult;
		if (testResult > 0.01)
			std::cout << "\nLongest Run of Ones in a Block Test passed.\n";
	}
	if ((MATRIX_CODE & code) == MATRIX_CODE)
	{
		testResult = binaryMatrixRankTest->execute(bitsToTest);
		std::cout << "\nBinary_Matrix P_value: " << testResult ;
		if (testResult > 0.01)
			std::cout << "\nBinary Matrix Rank Test passed.\n\n";
	}
	if ((FFT_CODE & code) == FFT_CODE)
	{
		//dftSpectralTest->execute(bitsToTest);
	}
	if ((NONOVERLAPPING_CODE & code) == NONOVERLAPPING_CODE)
	{
		nonOverlappingTemplateMatchingTest->execute(bitsToTest);
	}
	if ((OVERLAPPING_CODE & code) == OVERLAPPING_CODE)
	{
		overlappingTemplateMatchingTest->execute(bitsToTest);
	}
	if ((UNIVERSAL_CODE & code) == UNIVERSAL_CODE)
	{
		testResult = universalTest->execute(bitsToTest);
		std::cout << "\nUniversal Statistics P_value: " << testResult;
		if (testResult > 0.01)
			std::cout << "\nMaurers Universal Test passed.\n";
	}
	if ((LINEARCOMPLEX_CODE & code) == LINEARCOMPLEX_CODE)
	{
		testResult = linearTest->execute(1000, bitsToTest); //blocksize 500 <= blocksize <= 5000 for valid chi_square	
		std::cout << "\nLinear Complexity P_value: " << testResult;
		if (testResult > 0.01)
			std::cout << "\nLinear Complexity Test passed.\n";

	}
	if ((SERIAL_CODE & code) == SERIAL_CODE)
	{
		
	}
	if ((ENTROPY_CODE & code) == ENTROPY_CODE)
	{
		
	}
	if ((SUMS_CPDE & code) == SUMS_CPDE)
	{
		
	}
	if ((EXCURSION_CODE & code) == EXCURSION_CODE)
	{
		
	}
	if ((VARIANT_CODE & code) == VARIANT_CODE)
	{
		
	}

}


void Control::initTests(bool* bitArray, int bitArraySize)
{
	this->monobitTest = new Monobit(bitArray, bitArraySize);
	this->blockfrequencyTest = new BlockFrequency(bitArray, bitArraySize);
	this->runsTest = new Runs(bitArray, bitArraySize);
	this->longestRunOfOnesTest = new LongestRunOfOnes(bitArray, bitArraySize);
	this->binaryMatrixRankTest = new BinaryMatrixRank(bitArray, bitArraySize);

	this->dftSpectralTest = new Dft_spectral(bitArray, bitArraySize);
	this->overlappingTemplateMatchingTest = new OverlappingTemplateMatching(bitArray, bitArraySize);
	this->nonOverlappingTemplateMatchingTest = new NonOverlappingTemplateMatching(bitArray, bitArraySize);

	this->universalTest = new Universal(bitArray, bitArraySize);
	this->linearTest = new LinearComplexity(bitArray, bitArraySize);
}


void Control::convertVector_to_Array()
{
	bitArray = new bool[bitArraySize];

	for (int i = 0; i < bitArraySize; i++)
	{
		if ((*bitVector)[i] == 1)
			bitArray[i] = true;
		else
			bitArray[i] = false;
	}
}





